package com.tlgen.spring.factory;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tlgen.spring.annotation.*;
import com.tlgen.spring.model.BeanDefinition;
import com.tlgen.spring.utils.ScanUtils;
import com.tlgen.spring.utils.SpeedSpringContextUtils;
import com.tlgen.spring.utils.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.tlgen.spring.utils.SpeedSpringReflectionUtils.invokeBeanMethod;
import static com.tlgen.spring.utils.SpeedSpringReflectionUtils.newInstanceInterface;
import static com.tlgen.spring.utils.YmlUtils.getValueFromYml;

public class SpeedBeanFactory {

    /**
     * 创建 bean 对象
     */
    public static void createBean() {
        List<String> classesWithAnnotation = getBeanClassList();
        List<BeanDefinition> beanDefinitionList = Lists.newArrayList();
        for (String className : classesWithAnnotation) {
            // 对 bean 进行属性填充
            populateBeanFieldValue(beanDefinitionList, className);
        }
        // 完善 bean 定义信息
        completeBeanDefinition(beanDefinitionList);
    }

    /**
     * 完善 bean 定义信息
     *
     * @param beanDefinitionList bean 引用链信息
     */
    private static void completeBeanDefinition(List<BeanDefinition> beanDefinitionList) {
        List<BeanDefinition> finalBeanDefinitionList = Lists.newArrayList();
        // 对所有 bean 类去重
        beanDefinitionList = beanDefinitionList.stream().distinct().collect(Collectors.toList());
        // 按 beanName 进行分组, 当单层定义的 bean 和内部被引用的 bean 同时出现时, 只获取内部被引用的 bean, 最终返回单层的 bean 定义信息
        Map<String, List<BeanDefinition>> map = beanDefinitionList.stream().collect(Collectors.groupingBy(BeanDefinition::getBeanName));
        for (Map.Entry<String, List<BeanDefinition>> entry : map.entrySet()) {
            if (entry.getValue().size() > 1) {
                // 获取 instance 长度最长的, 被赋值最完整的
                BeanDefinition beanDefinitionOne = entry.getValue()
                        .stream().max(Comparator.comparing(x -> JSONObject.toJSONString(x.getInstance()).length())).get();
                finalBeanDefinitionList.add(beanDefinitionOne);
            } else {
                finalBeanDefinitionList.add(entry.getValue().get(0));
            }
        }
        // 保存最终的 bean 到单例类
        SpeedDefaultListBeanFactory.getInstance().setBeanDefinitionList(finalBeanDefinitionList);
    }

    /**
     * 填充 bean 属性值
     *
     * @param beanDefinitionList
     * @param className
     */
    private static void populateBeanFieldValue(List<BeanDefinition> beanDefinitionList, String className) {
        try {
            BeanDefinition beanDefinition = new BeanDefinition();
            Class<?> aClass = Class.forName(className);
            Constructor<?> constructor = aClass.getConstructor();
            Object instance = constructor.newInstance();

            // 设置属性值
            for (Field declaredField : aClass.getDeclaredFields()) {
                declaredField.setAccessible(true);
                Value annotation = declaredField.getAnnotation(Value.class);
                if (Objects.nonNull(annotation)) {
                    // 根据 yml 文件配置获取属性值
                    Object valueFromYml = getValueFromYml(annotation);
                    declaredField.set(instance, valueFromYml);
                }
                // @Autowired 注入引用的 Bean
                Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                if (Objects.nonNull(autowiredAnnotation)) {
                    Class<?> type = declaredField.getType();
                    BeanDefinition definition = new BeanDefinition();
                    String interfaceClassName = type.getTypeName(); // 一定是接口类型
                    definition.setClassName(interfaceClassName);
                    definition.setBeanName(type.getTypeName());
                    // 如果从单例池中没有找到, 那么直接创建, 因为有可能是其他 bean 在初始化时引用了这个接口 bean, 已经创建过
                    Object object = SpeedSpringContextUtils.getBean(type);
                    if (Objects.nonNull(object)) {
                        declaredField.set(instance, object);
                    } else {
                        // 如果是接口类型, 直接找到接口的实现类进行实例化
                        Object interfaceInstance = newInstanceInterface(beanDefinitionList, interfaceClassName);
                        declaredField.set(instance, interfaceInstance);
                    }
                }
            }

            beanDefinition.setClassName(className);
            beanDefinition.setBeanName(StringUtils.getSimpleClassName(className));
            beanDefinition.setInstance(instance);

            // @Bean 声明的 Bean
            for (Method declaredMethod : aClass.getDeclaredMethods()) {
                Bean annotation = declaredMethod.getAnnotation(Bean.class);
                if (Objects.nonNull(annotation)) {
                    // 获取返回的类型, 即 Bean 的类型
                    Class<?> returnClassType = declaredMethod.getReturnType();
                    // 声明的 bean 类型
                    String declaredBeanName = returnClassType.getTypeName();
                    BeanDefinition newBeanDefinition = new BeanDefinition();
                    newBeanDefinition.setBeanName(StringUtils.getSimpleClassName(declaredBeanName));
                    newBeanDefinition.setClassName(declaredBeanName);
                    // 填充 bean 属性值
                    populateBeanFieldValue(beanDefinitionList, declaredBeanName);
                    // 调用 @Bean 的方法结果作为 Bean
                    Object newInstance = invokeBeanMethod(className, declaredBeanName);
                    newBeanDefinition.setInstance(newInstance);
                    beanDefinitionList.add(newBeanDefinition);
                }
            }

            beanDefinitionList.add(beanDefinition);

        } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException |
                 IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据 bean 注解获取 bean 类集合
     *
     * @return
     */
    private static List<String> getBeanClassList() {
        List<String> classes = Lists.newArrayList();
        List<String> configurationClasses = ScanUtils.getClassesWithAnnotation(Configuration.class);
        List<String> componentClasses = ScanUtils.getClassesWithAnnotation(Component.class);
        List<String> restControllerClasses = ScanUtils.getClassesWithAnnotation(RestController.class);
        classes.addAll(configurationClasses);
        classes.addAll(componentClasses);
        classes.addAll(restControllerClasses);
        return classes.stream().distinct().collect(Collectors.toList());
    }

}
